package chat

import java.io.PrintStream
import java.net.ServerSocket
import java.net.Socket
import java.util.*
import kotlin.concurrent.thread

//端口9999
fun main(args : Array<String>){
    Server(9999).start()
}


class Server(val port : Int){

    private var connectedClients : List<ChatClient> = emptyList()       //客户端列表
    private var listener : Thread? = null     //监听线程

    fun start(){
        listener = thread {     //启动监听客户连接线程，在后台运行
            listenConnections()
        }


        //循环监听消息
        while (true){
            var messages = emptyList<Message>()  //构造一个空的消息列表

            val clientsClosed = connectedClients.filter { it.isClosed }     //筛选出关闭掉的客户连接
            if (clientsClosed.size > 0) {       //如果有
                connectedClients = connectedClients.filter { !it.isClosed }     //保留仍在连接的

                clientsClosed.forEach { client ->
                    messages += Message("系统", "${client.username}退出聊天")
                }
            }


            print("")  //这里有些奇怪，不佳的话就不会显示用户发送的消息
            connectedClients.forEach { client ->
                //println("客户${client.username}在线")
                if (client.hasMessage){
                    println("客户${client.username}发送消息")
                    messages += client.readMessage()
                }
            }

            //send messages
            messages.forEach { message ->
                connectedClients     //把这些消息显示给其他用户
                        .filter { it.username != message.usernameFrom && !it.isClosed }
                        .forEach { client -> client.writeMessage(message) }
            }
        }

    }

    private fun listenConnections(){

        ServerSocket(port).use { server ->
            println("服务进程运行在 ${server.localPort}号端口!")

            while (true) {
                val client = server.accept()            //监听客户连接
                println("有客户尝试连接 ${client.inetAddress.hostAddress}")

                val connectMessage = getClientName(client)

                val hasDuplicatedUsername = connectedClients.any { it.username == connectMessage.username }   //防止相同用户连接
                if(hasDuplicatedUsername){
                    reject(client)   //拒绝
                    continue    //这个continue极其重要，不然下面会报错！！！
                }

                val privateListener = ServerSocket(0)   //分配一个匿名端口，创建一个套接字，作为服务端的读，客户端的写
                sendSuccess(client, privateListener.localPort)  //向连接成功的用户发送独立的端口号


                val readClient = privateListener.accept()
                privateListener.close()

                val chatClient = ChatClient(connectMessage.username, readClient, client)  //注册新的用户
                connectedClients += chatClient

                //发送系统消息
                connectedClients.forEach { client ->
                    client.writeMessage(Message("系统", "${chatClient.username} 连接成功"))
                }
            }
        }

    }


    private fun sendSuccess(client: Socket, localPort: Int) {
        PrintStream(client.outputStream).println(localPort)
    }

    //拒绝相同名称用户
    private fun reject(client : Socket){
        PrintStream(client.outputStream).println("您的名称已被占用，即将退出")
        client.close()
    }


    //得到客户名
    private fun getClientName(client : Socket) : ConnectMessage {
        val scanner = Scanner(client.inputStream)
        val username = scanner.nextLine()   //用户名只有一行
        return ConnectMessage(username)
    }


}

class ChatClient(val username: String,
                 val readClient: Socket,  //两个socket
                 val writeClient : Socket){

    var isClosed : Boolean = false
        private set

    var hasMessage = false
        private set

    var readLine : String? = null
        private set

    init {
        thread {
            while (!readClient.isClosed){
                val s = Scanner(readClient.inputStream)
                while (s.hasNextLine()){        //客户端有消息
                    synchronized(hasMessage){
                        readLine = s.nextLine()
                        hasMessage = true           //true
                    }
                }
                s.close()       //无消息时关闭扫描器
            }
            println("${username}退出")
            readClient.close()      //客户退出时，当用户退出时会自动触发么？
            writeClient.close()
            isClosed = true
        }
    }


    fun readMessage() : Message {
        synchronized(hasMessage) {   //线程同步锁

            //readLine = usernameFrom::mensagem

            val messageParts = readLine!!.split("::")
            val message = Message(messageParts[0], messageParts[1])

            readLine = null         //重要
            hasMessage = false

            return message
        }
    }

    fun writeMessage(message : Message) {
        val ps = PrintStream(writeClient.outputStream)
        ps.println(message)
    }

}


data class ConnectMessage(val username:String)

//定义Message格式，是一种约定
data class Message(val usernameFrom:String,
                   val text : String) {

    override fun toString(): String {
        return "$usernameFrom::$text"
    }

    //companion pbject就像是static
    companion object {
        fun parseMessage(string : String) : Message {           //以::为分割
            val stringParts = string.split("::")
            return Message(stringParts[0], stringParts[1])
        }
    }

}